/*
 * Code for blog.techhead.biz
 * Distributed under BSD-style license
 */
package biz.techhead.funcy.ref;

import biz.techhead.funcy.Func;
import biz.techhead.funcy.FuncE;
import biz.techhead.funcy.Funcs;
import biz.techhead.funcy.Tuples.T0;

/**
 *
 * @author Jonathan Hawkes <jhawkes at techhead.biz>
 */
public class References {
    private References() {}

    @SuppressWarnings("unchecked")
    public static <T> Func<Reference<T>,T> strong() {
        return ( Func<Reference<T>,T> ) strongRefMaker;
    }

    public static <T> Reference<T> strong(final T val) {
        return new Reference<T>() {
            public T get() {
                return val;
            }
        };
    }

    public static <T> Reference<T> strong(FuncE<? extends T,
                                                ? super T0,
                                                ? extends Throwable> exp) {
        return new StrongFuncyReference<T>(exp);
    }

    @SuppressWarnings("unchecked")
    public static <T> Func<SoftReference<T>,T> soft() {
        return ( Func<SoftReference<T>,T> ) softRefMaker;
    }

    public static <T> SoftReference<T> soft(T val) {
        return new SoftReference<T>(val);
    }

    public static <T> Reference<T> soft(FuncE<? extends T,
                                              ? super T0,
                                              ? extends Throwable> exp) {
        return new NotStrongFuncyReference<T>(exp, References.<T>soft());
    }

    @SuppressWarnings("unchecked")
    public static <T> Func<WeakReference<T>,T> weak() {
        return ( Func<WeakReference<T>,T> ) weakRefMaker;
    }

    public static <T> WeakReference<T> weak(T val) {
        return new WeakReference<T>(val);
    }

    public static <T> Reference<T> weak(FuncE<? extends T,
                                              ? super T0,
                                              ? extends Throwable> exp) {
        return new NotStrongFuncyReference<T>(exp, References.<T>weak());
    }

    public static class SoftReference<T> extends java.lang.ref.SoftReference<T>
            implements Reference<T> {
        SoftReference(T val) {
            super(val);
        }
    }

    public static class WeakReference<T> extends java.lang.ref.WeakReference<T>
            implements Reference<T> {
        WeakReference(T val) {
            super(val);
        }
    }

    private static class StrongFuncyReference<T> extends LazyReference<T> {

        StrongFuncyReference(FuncE<? extends T,
                                   ? super T0,
                                   ? extends Throwable> exp) {
            super();
            this.exp = Funcs.asFunc(exp);
        }

        @Override
        protected T initValue() {
            T value = Funcs.call(exp);
            exp = null;
            return value;
        }
        
        private Func<? extends T,? super T0> exp;
    }
    
    private static class NotStrongFuncyReference<T> extends LazyReference<T> {

        NotStrongFuncyReference(FuncE<? extends T,
                                      ? super T0,
                                      ? extends Throwable> exp,
                                FuncE<? extends Reference<T>,
                                   T, ? extends Throwable> refMaker) {
            super(refMaker);
            this.exp = Funcs.asFunc(exp);
        }

        @Override
        protected T initValue() {
            return Funcs.call(exp);
        }
        
        private final Func<? extends T,? super T0> exp;
    }

    @SuppressWarnings("unchecked")
    private static final Func strongRefMaker = new Func() {
        public Object call(Object val) {
            return strong(val);
        }
    };

    @SuppressWarnings("unchecked")
    private static final Func softRefMaker = new Func() {
        public Object call(Object val) {
            return soft(val);
        }
    };

    @SuppressWarnings("unchecked")
    private static final Func weakRefMaker = new Func() {
        public Object call(Object val) {
            return weak(val);
        }
    };
}
